/**
 * Claude 转发配置服务
 * 管理全局 Claude Code 限制和会话绑定配置
 */

const redis = require('../models/redis')
const logger = require('../utils/logger')

const CONFIG_KEY = 'claude_relay_config'
const SESSION_BINDING_PREFIX = 'original_session_binding:'

// 默认配置
const DEFAULT_CONFIG = {
  claudeCodeOnlyEnabled: false,
  globalSessionBindingEnabled: false,
  sessionBindingErrorMessage: '你的本地session已污染，请清理后使用。',
  sessionBindingTtlDays: 30, // 会话绑定 TTL（天），默认30天
  updatedAt: null,
  updatedBy: null
}

// 内存缓存（避免频繁 Redis 查询）
let configCache = null
let configCacheTime = 0
const CONFIG_CACHE_TTL = 60000 // 1分钟缓存

class ClaudeRelayConfigService {
  /**
   * 从 metadata.user_id 中提取原始 sessionId
   * 格式: user_{64位十六进制}_account__session_{uuid}
   * @param {Object} requestBody - 请求体
   * @returns {string|null} 原始 sessionId 或 null
   */
  extractOriginalSessionId(requestBody) {
    if (!requestBody?.metadata?.user_id) {
      return null
    }

    const userId = requestBody.metadata.user_id
    const match = userId.match(/session_([a-f0-9-]{36})$/i)
    return match ? match[1] : null
  }

  /**
   * 获取配置（带缓存）
   * @returns {Promise<Object>} 配置对象
   */
  async getConfig() {
    try {
      // 检查缓存
      if (configCache && Date.now() - configCacheTime < CONFIG_CACHE_TTL) {
        return configCache
      }

      const client = redis.getClient()
      if (!client) {
        logger.warn('⚠️ Redis not connected, using default config')
        return { ...DEFAULT_CONFIG }
      }

      const data = await client.get(CONFIG_KEY)

      if (data) {
        configCache = { ...DEFAULT_CONFIG, ...JSON.parse(data) }
      } else {
        configCache = { ...DEFAULT_CONFIG }
      }

      configCacheTime = Date.now()
      return configCache
    } catch (error) {
      logger.error('❌ Failed to get Claude relay config:', error)
      return { ...DEFAULT_CONFIG }
    }
  }

  /**
   * 更新配置
   * @param {Object} newConfig - 新配置
   * @param {string} updatedBy - 更新者
   * @returns {Promise<Object>} 更新后的配置
   */
  async updateConfig(newConfig, updatedBy) {
    try {
      const client = redis.getClientSafe()
      const currentConfig = await this.getConfig()

      const updatedConfig = {
        ...currentConfig,
        ...newConfig,
        updatedAt: new Date().toISOString(),
        updatedBy
      }

      await client.set(CONFIG_KEY, JSON.stringify(updatedConfig))

      // 更新缓存
      configCache = updatedConfig
      configCacheTime = Date.now()

      logger.info(`✅ Claude relay config updated by ${updatedBy}:`, {
        claudeCodeOnlyEnabled: updatedConfig.claudeCodeOnlyEnabled,
        globalSessionBindingEnabled: updatedConfig.globalSessionBindingEnabled
      })

      return updatedConfig
    } catch (error) {
      logger.error('❌ Failed to update Claude relay config:', error)
      throw error
    }
  }

  /**
   * 检查是否启用全局 Claude Code 限制
   * @returns {Promise<boolean>}
   */
  async isClaudeCodeOnlyEnabled() {
    const cfg = await this.getConfig()
    return cfg.claudeCodeOnlyEnabled === true
  }

  /**
   * 检查是否启用全局会话绑定
   * @returns {Promise<boolean>}
   */
  async isGlobalSessionBindingEnabled() {
    const cfg = await this.getConfig()
    return cfg.globalSessionBindingEnabled === true
  }

  /**
   * 获取会话绑定错误信息
   * @returns {Promise<string>}
   */
  async getSessionBindingErrorMessage() {
    const cfg = await this.getConfig()
    return cfg.sessionBindingErrorMessage || DEFAULT_CONFIG.sessionBindingErrorMessage
  }

  /**
   * 获取原始会话绑定
   * @param {string} originalSessionId - 原始会话ID
   * @returns {Promise<Object|null>} 绑定信息或 null
   */
  async getOriginalSessionBinding(originalSessionId) {
    if (!originalSessionId) {
      return null
    }

    try {
      const client = redis.getClient()
      if (!client) {
        return null
      }

      const key = `${SESSION_BINDING_PREFIX}${originalSessionId}`
      const data = await client.get(key)

      if (data) {
        return JSON.parse(data)
      }
      return null
    } catch (error) {
      logger.error(`❌ Failed to get session binding for ${originalSessionId}:`, error)
      return null
    }
  }

  /**
   * 设置原始会话绑定
   * @param {string} originalSessionId - 原始会话ID
   * @param {string} accountId - 账户ID
   * @param {string} accountType - 账户类型
   * @returns {Promise<Object>} 绑定信息
   */
  async setOriginalSessionBinding(originalSessionId, accountId, accountType) {
    if (!originalSessionId || !accountId || !accountType) {
      throw new Error('Invalid parameters for session binding')
    }

    try {
      const client = redis.getClientSafe()
      const key = `${SESSION_BINDING_PREFIX}${originalSessionId}`

      const binding = {
        accountId,
        accountType,
        createdAt: new Date().toISOString(),
        lastUsedAt: new Date().toISOString()
      }

      // 使用配置的 TTL（默认30天）
      const cfg = await this.getConfig()
      const ttlDays = cfg.sessionBindingTtlDays || DEFAULT_CONFIG.sessionBindingTtlDays
      const ttlSeconds = Math.floor(ttlDays * 24 * 3600)

      await client.set(key, JSON.stringify(binding), 'EX', ttlSeconds)

      logger.info(
        `🔗 Session binding created: ${originalSessionId} -> ${accountId} (${accountType})`
      )

      return binding
    } catch (error) {
      logger.error(`❌ Failed to set session binding for ${originalSessionId}:`, error)
      throw error
    }
  }

  /**
   * 更新会话绑定的最后使用时间（续期）
   * @param {string} originalSessionId - 原始会话ID
   */
  async touchOriginalSessionBinding(originalSessionId) {
    if (!originalSessionId) {
      return
    }

    try {
      const binding = await this.getOriginalSessionBinding(originalSessionId)
      if (!binding) {
        return
      }

      binding.lastUsedAt = new Date().toISOString()

      const client = redis.getClientSafe()
      const key = `${SESSION_BINDING_PREFIX}${originalSessionId}`

      // 使用配置的 TTL（默认30天）
      const cfg = await this.getConfig()
      const ttlDays = cfg.sessionBindingTtlDays || DEFAULT_CONFIG.sessionBindingTtlDays
      const ttlSeconds = Math.floor(ttlDays * 24 * 3600)

      await client.set(key, JSON.stringify(binding), 'EX', ttlSeconds)
    } catch (error) {
      logger.warn(`⚠️ Failed to touch session binding for ${originalSessionId}:`, error)
    }
  }

  /**
   * 检查原始会话是否已绑定
   * @param {string} originalSessionId - 原始会话ID
   * @returns {Promise<boolean>}
   */
  async isOriginalSessionBound(originalSessionId) {
    const binding = await this.getOriginalSessionBinding(originalSessionId)
    return binding !== null
  }

  /**
   * 验证绑定的账户是否可用
   * @param {Object} binding - 绑定信息
   * @returns {Promise<boolean>}
   */
  async validateBoundAccount(binding) {
    if (!binding || !binding.accountId || !binding.accountType) {
      return false
    }

    try {
      const { accountType } = binding
      const { accountId } = binding

      let accountService
      switch (accountType) {
        case 'claude-official':
          accountService = require('./claudeAccountService')
          break
        case 'claude-console':
          accountService = require('./claudeConsoleAccountService')
          break
        case 'bedrock':
          accountService = require('./bedrockAccountService')
          break
        case 'ccr':
          accountService = require('./ccrAccountService')
          break
        default:
          logger.warn(`Unknown account type for validation: ${accountType}`)
          return false
      }

      const account = await accountService.getAccount(accountId)

      if (!account || !account.success || !account.data) {
        logger.warn(`Session binding account not found: ${accountId} (${accountType})`)
        return false
      }

      const accountData = account.data

      // 检查账户是否激活
      if (accountData.isActive === false || accountData.isActive === 'false') {
        logger.warn(
          `Session binding account not active: ${accountId} (${accountType}), isActive: ${accountData.isActive}`
        )
        return false
      }

      // 检查账户状态（如果存在）
      if (accountData.status && accountData.status === 'error') {
        logger.warn(
          `Session binding account has error status: ${accountId} (${accountType}), status: ${accountData.status}`
        )
        return false
      }

      return true
    } catch (error) {
      logger.error(`❌ Failed to validate bound account ${binding.accountId}:`, error)
      return false
    }
  }

  /**
   * 验证新会话请求
   * @param {Object} requestBody - 请求体
   * @param {string} originalSessionId - 原始会话ID
   * @returns {Promise<Object>} { valid: boolean, error?: string, binding?: object, isNewSession?: boolean }
   */
  async validateNewSession(requestBody, originalSessionId) {
    const cfg = await this.getConfig()

    if (!cfg.globalSessionBindingEnabled) {
      return { valid: true }
    }

    // 如果没有 sessionId，跳过验证（可能是非 Claude Code 客户端）
    if (!originalSessionId) {
      return { valid: true }
    }

    const existingBinding = await this.getOriginalSessionBinding(originalSessionId)

    // 如果会话已存在绑定
    if (existingBinding) {
      // ⚠️ 只有 claude-official 类型账户受全局会话绑定限制
      // 其他类型（bedrock, ccr, claude-console等）忽略绑定，走正常调度
      if (existingBinding.accountType !== 'claude-official') {
        logger.info(
          `🔗 Session binding ignored for non-official account type: ${existingBinding.accountType}`
        )
        return { valid: true }
      }

      const accountValid = await this.validateBoundAccount(existingBinding)

      if (!accountValid) {
        return {
          valid: false,
          error: cfg.sessionBindingErrorMessage,
          code: 'SESSION_BINDING_INVALID'
        }
      }

      // 续期
      await this.touchOriginalSessionBinding(originalSessionId)

      // 已有绑定，允许继续（这是正常的会话延续）
      return { valid: true, binding: existingBinding }
    }

    // 没有绑定，是新会话
    // 注意：messages.length 检查在此处无法执行，因为我们不知道最终会调度到哪种账户类型
    // 绑定会在调度后创建，仅针对 claude-official 账户
    return { valid: true, isNewSession: true }
  }

  /**
   * 删除原始会话绑定
   * @param {string} originalSessionId - 原始会话ID
   */
  async deleteOriginalSessionBinding(originalSessionId) {
    if (!originalSessionId) {
      return
    }

    try {
      const client = redis.getClient()
      if (!client) {
        return
      }

      const key = `${SESSION_BINDING_PREFIX}${originalSessionId}`
      await client.del(key)
      logger.info(`🗑️ Session binding deleted: ${originalSessionId}`)
    } catch (error) {
      logger.error(`❌ Failed to delete session binding for ${originalSessionId}:`, error)
    }
  }

  /**
   * 获取会话绑定统计
   * @returns {Promise<Object>}
   */
  async getSessionBindingStats() {
    try {
      const client = redis.getClient()
      if (!client) {
        return { totalBindings: 0 }
      }

      let cursor = '0'
      let count = 0

      do {
        const [newCursor, keys] = await client.scan(
          cursor,
          'MATCH',
          `${SESSION_BINDING_PREFIX}*`,
          'COUNT',
          100
        )
        cursor = newCursor
        count += keys.length
      } while (cursor !== '0')

      return {
        totalBindings: count
      }
    } catch (error) {
      logger.error('❌ Failed to get session binding stats:', error)
      return { totalBindings: 0 }
    }
  }

  /**
   * 清除配置缓存（用于测试或强制刷新）
   */
  clearCache() {
    configCache = null
    configCacheTime = 0
  }
}

module.exports = new ClaudeRelayConfigService()
